﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Grishko.SerilizeTools;
using Grishko.DataTier.SqlSentence;

namespace Grishko.DataTier.MetaInfo
{
    /// <summary>
    /// Класс содержащий метаинформацию о экземпяре. 
    /// Описание объекта и дочерних объектов  в виде SrchEntityItemInfoList    
    /// Описание свойств объекта и дочерних объектов  в виде SrchEntityList
    /// </summary>
    /// 
    [TagNameInfo(TagName = "SrchInfo")] 
   public  class SrchInfo:EntitySingle 
    {
       private SrchObjectPropertyStorage _SrchObjectPropertyStorage = new SrchObjectPropertyStorage();       
       /// <summary>
       /// Описание свойств объекта 
       /// </summary>
       public SrchObjectPropertyStorage SrchObjectPropertyStorage { get { return _SrchObjectPropertyStorage; } }

       private SrchObjectPropertyTreeStorage _SrchObjectPropertyTreeStorage = new SrchObjectPropertyTreeStorage();

       /// <summary>
       /// Древовидные свойства объекта 
       /// </summary> 
       public SrchObjectPropertyTreeStorage SrchObjectPropertyTreeStorage { get { return _SrchObjectPropertyTreeStorage; } }
       

       private SrchStorageLinks _SrchStorageLinks = new SrchStorageLinks();
       /// <summary>
       /// Описание объекта
       /// </summary>
       public SrchStorageLinks SrchStorageLinks { get { return _SrchStorageLinks; } }

       
        public override string ToString()
       {
           return ToXml(false);
       }

     

       public static   SrchStorageLinksItem GetSrchStorageLinksItem(EntitySingle obj, string RootId, string XPatch)
       {
           ////Пишем в линк информацию о объекте 
           SrchStorageLinksItem LinkInfo = new SrchStorageLinksItem();
           LinkInfo.ID = ((EntitySingle)obj).ID;
           LinkInfo.ObjectPref = ((EntitySingle)obj).TagName;
           LinkInfo.StorageObjectID = RootId;
           LinkInfo.XPATH = XPatch;
           return LinkInfo;
       }
      
        /// <summary>
       /// Сформировать описание свойств  поля - значение древовидноо справочника 
       /// </summary>
       /// <param name="PropInfo"></param>
       /// <param name="PropertyValue"></param>
       /// <param name="PropertyParentObject"></param>
       /// <returns></returns>
       public static EntityList   GetSrchTreeEntityItem  
                                                                     (SrchItemInfo SrchPropInfo, 
                                                                      PropertyInfo PropInfo, 
                                                                      TreeValueProperty  PropertyValue, 
                                                                      EntitySingle  PropertyParentObject)
       {
           EntityList res = new EntityList();
           SrchTreeEntityItem itm = new SrchTreeEntityItem();
           ////Вершина
           itm.ObjectID = ((EntitySingle)PropertyParentObject).ID;
           itm.ObjectTagName = ((EntitySingle)PropertyParentObject).TagName;
           itm.PropertyName = PropInfo.Name;
           itm.PropertyValue = PropertyValue.ID;
           itm.ParentProperty = null;
           res.Add(itm);



           foreach (DictionaryItem p in PropertyValue.ChildNodes) 
           {
            itm = new SrchTreeEntityItem();
            itm.ObjectID = ((EntitySingle)PropertyParentObject).ID;
            itm.ObjectTagName = ((EntitySingle)PropertyParentObject).TagName;
            itm.PropertyName = PropInfo.Name;
            itm.PropertyValue =p.Value.ToString ();
            itm.ParentProperty = p.ParamID;
            res.Add(itm);

           }
           return res;
       }

       /// <summary>
       /// Сформировать описание свойств объектного поля
       /// </summary>
       /// <param name="PropInfo"></param>
       /// <param name="PropertyValue"></param>
       /// <param name="PropertyParentObject"></param>
       /// <returns></returns>
       public static SrchObjectPropertyStorageItem GetSrchEntityItem(SrchItemInfo SrchPropInfo, PropertyInfo PropInfo, object PropertyValue, object PropertyParentObject)
       {
           SrchObjectPropertyStorageItem  res = new SrchObjectPropertyStorageItem();
           res.ObjectID = ((EntitySingle)PropertyParentObject).ID;
           res.ObjectTagName = ((EntitySingle)PropertyParentObject).TagName;
           res.PropertyName = PropInfo.Name;
           res.PropertyType = PropInfo.PropertyType.ToString();
           res.PropertyValue = ((EntitySingle)PropertyValue).ID;
           res.TxtPropertyName = SrchPropInfo.TxtPropertyName;
           res.TxtPropertyValue = res.PropertyValue;
           res.IsObject = true;
           res.ValueIsSqlFunction = SrchPropInfo.ValueIsSqlFunction;
           return res;
       }
       
       /// <summary>
       /// Сформировать запись описание объектного свойства 
       /// </summary>
       /// <param name="srch_atr"> Значение [SrchItemInfo")]       </param>
       /// <param name="CollectionPropertyName">Шаблон для формирвания имени свойства</param>
       /// <param name="ItemType">Тип свойства</param>
       /// <param name="PropertyValue">Значение свойства</param>
       /// <param name="PropertyParentObject"></param>
       /// <returns></returns>
       public static SrchObjectPropertyStorageItem GetSrchEntityCollectionItem(SrchItemInfo srch_atr,string CollectionPropertyName, Type ItemType, object PropertyValue, object PropertyParentObject)
       {
           string ItemID = ((EntitySingle)PropertyValue).ID;
           SrchObjectPropertyStorageItem res = new SrchObjectPropertyStorageItem();
           res.ObjectID = ((EntitySingle)PropertyParentObject).ID;
           string[] s = { CollectionPropertyName, ItemID };


           res.ObjectTagName = ((EntitySingle)PropertyParentObject).TagName;
           res.PropertyName = string.Format("{0}", s);
           res.PropertyType = ItemType.ToString();
           res.PropertyValue = ItemID;
           
           res.TxtPropertyName = (srch_atr.TxtPropertyName==null?res.PropertyName: srch_atr.TxtPropertyName) ;
           res.TxtPropertyName = (res.TxtPropertyName == SrchItemInfo.EmptyString ? String.Empty : res.TxtPropertyName); 

           res.TxtPropertyValue = res.PropertyValue;
           res.IsObject = true;
           res.ValueIsSqlFunction = srch_atr.ValueIsSqlFunction; 
           return res;
       }

       /// <summary>
       /// Сформировать описание свойств линейного  поля
       /// </summary>
       /// <param name="SrchPropInfo"></param>
       /// <param name="PropInfo"></param>
       /// <param name="PropertyValue"></param>
       /// <param name="ObjectID"></param>
       /// <returns></returns>
       public static  SrchObjectPropertyStorageItem GetSrch_AttProperty(string ObjectTagName, SrchItemInfo SrchPropInfo, PropertyInfo PropInfo, object PropertyValue, string ObjectID)
       {
           //Пустые свойства не пишем
           if (null == PropertyValue)
                    return null;
           if(String.IsNullOrEmpty( PropertyValue.ToString()))
                    return null;

           SrchObjectPropertyStorageItem res = new SrchObjectPropertyStorageItem();
           res.ObjectID = ObjectID;
           res.ObjectTagName = ObjectTagName;

           res.PropertyName = PropInfo.Name;
           res.PropertyType = PropInfo.PropertyType.ToString();
           res.PropertyValue = PropertyValue.ToString();
           
           res.TxtPropertyName = (SrchPropInfo.TxtPropertyName == null ? res.PropertyName : SrchPropInfo.TxtPropertyName);
           res.TxtPropertyName = (res.TxtPropertyName == SrchItemInfo.EmptyString ? String.Empty : res.TxtPropertyName); 
           
           res.TxtPropertyValue = res.PropertyValue;
           res.IsObject = false;
           res.ValueIsSqlFunction =SrchPropInfo.ValueIsSqlFunction ;
           return res;

       }
       
       //public  static List<WhereSentenceWord.SentenceWordField> GetSentenceDictionary(Type EntityType)
       //{
       //    List<WhereSentenceWord.SentenceWordField> Res = new List<WhereSentenceWord.SentenceWordField>();

       //    GetWordField(Res, EntityType, string.Empty, string.Empty);
       //    return Res;
       //}

       public static List<Word> GetSentenceDictionary(Type EntityType)
       {
           List<Word> Res = new List<Word>();

           GetWordField(Res, EntityType, string.Empty, string.Empty,string .Empty );
           return Res;
       }

       // рекурсивно обойти тип  и сформировать список  LinkFieldAttribute
       public static List<LinkFieldAttribute> GetLinkFieldAttributes(Type entityType)
       {
           List<LinkFieldAttribute> result = new List<LinkFieldAttribute>();
           GetLinkFieldAttribute(result, entityType);
            return result ;
        }


        /// <summary>
       /// Вернуть набор слов - исключив BanList 
        /// </summary>
        /// <param name="EntityType"></param>
        /// <param name="BanList"></param>
        /// <returns></returns>
       public static List<Word> GetSentenceDictionary(Type EntityType,string[] BanList )
       {
           Func<Word,bool> CheckBan = delegate(Word w)
           {
               return true;
           };

           List<Word> Res = new List<Word>();
           List<Word> result = new List<Word>();
           GetWordField(Res, EntityType, string.Empty, string.Empty,string .Empty );

           if (null == BanList)
                        return Res;

           foreach (Word i in Res)
           {
               foreach (string s in BanList)
               {
                   if (i.PropertyName.IndexOf (s)==-1)
                   {
                       result.Add(i);
                   }
               }
           }

           return result ;
       }

        

       /// <summary>
       /// Возвращает набор типа Contract.Manager.ID
       /// </summary>
       /// <param name="EntityType"></param>
       /// <returns></returns>
       public static List<String> GetSentenceDictionaryKeys(Type EntityType)
       {
           List<string> Res = new List<string>();

           GetWordFieldKeys(Res, EntityType, string.Empty, false);
           return Res;
       }
       /// <summary>
       /// Набор строк Контракт.Менеджер.Документы.Номер
       /// </summary>
       /// <param name="EntityType"></param>
       /// <returns></returns>
       public static List<String> GetSentenceDictionaryTxtKeys(Type EntityType)
       {
           List<string> Res = new List<string>();

           GetWordFieldKeys(Res, EntityType, string.Empty, true);
           return Res;
       }
       /// <summary>
       /// Вернуть атрибут TagNameInfo
    /// </summary>
    /// <param name="EntityType"></param>
    /// <returns></returns>
       public static TagNameInfo GetTagNameInfo(Type EntityType)
       {  
           object[] r = EntityType.GetCustomAttributes(typeof(TagNameInfo), true);
           if (0 == r.Length)
                         return null;

           TagNameInfo TagNameInfo = (TagNameInfo)r[0];
           return TagNameInfo;

       }

       /// <summary>
       /// Вернуть атрибут SrchItemInfo
       /// </summary>
       /// <param name="EntityType"></param>
       /// <returns></returns>
 
       public static SrchItemInfo GetSrchInfo(Type EntityType)
       {
           object[] r = EntityType.GetCustomAttributes(typeof(SrchItemInfo), true);
           SrchItemInfo TagNameInfo = (SrchItemInfo)r[0];
           return TagNameInfo;

       }

       /// <summary>
       /// Объекты этого информация по этому типу не пишется в БД поиска
       /// </summary>
       /// <param name="EntityType"></param>
       /// <returns></returns>
       public static bool  IsNotSrchStorageLinksObject (Type EntityType)
       {
           return EntityType.IsDefined (typeof(IsNotSrchStorageLinksObject),true );            
       }

       /// <summary>
       /// Вернуть SrchItemInfo для поля
       /// </summary>
       /// <param name="PropPnfo"></param>
       /// <returns></returns>
        public static SrchItemInfo GetSrchInfo(PropertyInfo PropPnfo)
       {
             object[] arr = PropPnfo.GetCustomAttributes(typeof(SrchItemInfo), true);
             SrchItemInfo srch_atr = (SrchItemInfo)arr[0];
             return srch_atr ;
       }

        /// <summary>
        /// попытаться получить атрибут LinkFieldAttribute у проперти
        /// </summary>
        /// <param name="propPnfo"></param>
        /// <returns></returns>
        public static LinkFieldAttribute GetLinkFieldAttribute(PropertyInfo propType)
        {
            LinkFieldAttribute srch_atr = null;
            object[] arr = propType.GetCustomAttributes(typeof(LinkFieldAttribute), true);
            if (0 == arr.Length)
                        return null;
            srch_atr = (LinkFieldAttribute)arr[0];
            srch_atr.PropertyField = propType.Name;
            return srch_atr;
        }       

       static Func<PropertyInfo, SrchItemInfo> GetSrchItemInfo = delegate(PropertyInfo PropInfo)
       {
           //выясняем какой тип поодерживает список
           object[] r = PropInfo.GetCustomAttributes(typeof(SrchItemInfo), true);
           SrchItemInfo SrchItemInfo = (SrchItemInfo)r[0];
           return SrchItemInfo;

       };

       /// <summary>
       ///  Вернуть ключи  коллекции возможных слов 
       /// </summary>
       /// <param name="Res"></param>
       /// <param name="EntityType"></param>
       /// <param name="IDPref"></param>
       /// <param name="isTxtKeys"></param> 
       static void GetWordFieldKeys(List<string> Res, Type EntityType, string IDPref, bool isTxtKeys)
       {
           //проходимся по свойствам - формируем для помеченных свойств SentenceWordField
           PropertyInfo[] AttProperties = EntityType.GetProperties();
           Type[] Incl = { typeof(SrchItemInfo) };


           foreach (PropertyInfo p in AttProperties)
           {


               if (p.IsDefined(typeof(SrchItemInfo), false))
               {
                   //Если это примитив - string number и т.п.  
                   if (ImAttProperty(p))
                   {
                       if (!isTxtKeys)
                           Res.Add(IDPref + (IDPref.Length == 0 ? "" : ".") + p.Name);
                       else
                       {
                           SrchItemInfo i = GetSrchItemInfo(p);
                           if (SrchItemInfo.EmptyString != i.TxtPropertyName)
                               Res.Add(IDPref + (IDPref.Length == 0 ? "" : ".") + i.TxtPropertyName);
                       }
                       continue;
                   }
               }
           }

           // Объектные свойства
           foreach (PropertyInfo p in AttProperties)
           {
               if (!p.IsDefined(typeof(SrchItemInfo), false))
                   continue;
               if (ImAttProperty(p))
                   continue;


               //Пытаемся определить TagName (считаем что это EntitySingle)
               if (p.PropertyType.IsDefined(typeof(TagNameInfo), false))
               {
                   if (!isTxtKeys)
                       GetWordFieldKeys(Res, p.PropertyType, IDPref + (IDPref.Length == 0 ? "" : ".") + p.Name, isTxtKeys);
                   else
                   {
                       SrchItemInfo i = GetSrchItemInfo(p);
                       GetWordFieldKeys(Res, p.PropertyType, IDPref + (IDPref.Length == 0 ? "" : ".") + i.TxtPropertyName, isTxtKeys);

                   }

               }
               else// Это список 
               {
                   //выясняем какой тип поодерживает список
                   object[] r = p.GetCustomAttributes(typeof(SrchItemInfo), true);
                   SrchItemInfo SrchItemInfo = (SrchItemInfo)r[0];

                   if (!isTxtKeys)
                       GetWordFieldKeys(Res, SrchItemInfo.ListType, IDPref + (IDPref.Length == 0 ? "" : ".") + p.Name, isTxtKeys);
                   else
                   {
                       SrchItemInfo i = GetSrchItemInfo(p);
                       GetWordFieldKeys(Res, SrchItemInfo.ListType, IDPref + (IDPref.Length == 0 ? "" : ".") + i.TxtPropertyName, isTxtKeys);
                   }


               }
           }
       }

       /// <summary>
       /// Вспомогательный пройтись по типу и сформировать набор слов 
       /// </summary>
       /// <param name="Res"></param>
       /// <param name="EntityType"></param>
       static void GetWordField(List<Word> Res, 
                                Type EntityType, 
                                string IDPref, 
                                string TagPatch, 
                                string TxtIDPref)
       {
           if (null == EntityType)
                                return;


           //проходимся по свойствам - формируем для помеченных свойств SentenceWordField
           PropertyInfo[] AttProperties = EntityType.GetProperties();
           Type[] Incl = { typeof(SrchItemInfo) };
           TagNameInfo tagName = Grishko.DataTier.MetaInfo.SrchInfo.GetTagNameInfo(EntityType);            

           foreach (PropertyInfo p in AttProperties)
           {
               if (p.IsDefined(typeof(SrchItemInfo), false))
               {
                   
                   //Если это примитив - string number и т.п.  
                   if (ImAttProperty(p))
                   {
                       SrchItemInfo i = GetSrchItemInfo(p);

                       Word r = new Word();
                       r.PropertyName  = IDPref + (IDPref.Length == 0 ? "" : ".") + p.Name;
                       
                       if(null!=tagName)
                           r.TagPatch = TagPatch + (TagPatch.Length == 0 ? "" : ".") + tagName.TagName;
                       
                       r.PropertyType = GetAttPropertyType(p);
                       r.SrchItemInfo = i;
                       
                       r.TxtKey = TxtIDPref + (TxtIDPref.Length == 0 ? "" : ".") + i.TxtPropertyName;
                       r.SrchItemInfo = i;
                       r.ParentTagName = tagName; 
                       Res.Add (r);
                       continue;
                   }
               }
           }

           // Объектные свойства
           foreach (PropertyInfo p in AttProperties)
           {               
               if (!p.IsDefined(typeof(SrchItemInfo), false))
                   continue;
               if (ImAttProperty(p))
                   continue;

               SrchItemInfo SrchItemInfo = GetSrchItemInfo(p);
               if (SrchItemInfo.TreeDictionaryTypeID!=string .Empty)
               {
                   SrchItemInfo i = GetSrchItemInfo(p);
                   Word r = new Word();
                   r.PropertyName  = IDPref + (IDPref.Length == 0 ? "" : ".") + p.Name;
                   r.PropertyType = SrchItemInfo.PropertyControlType.Tree;
                   r.SrchItemInfo = i;                   
                   r.TxtKey = TxtIDPref + (TxtIDPref.Length == 0 ? "" : ".") + i.TxtPropertyName;
                   r.SrchItemInfo = i;
                   r.ParentTagName = tagName;
                   r.TagPatch = TagPatch + (TagPatch.Length == 0 ? "" : ".") + tagName.TagName;
                   
                   Res.Add(r);
                   continue;
               }



               //Пытаемся определить TagName (считаем что это EntitySingle)
               if (p.PropertyType.IsDefined(typeof(TagNameInfo), false))
               {
                   GetWordField(Res, p.PropertyType,
                                IDPref + (IDPref.Length == 0 ? "" : ".") + p.Name,
                                TagPatch + (TagPatch.Length ==0? "" : ".") + tagName.TagName,
                                TxtIDPref + (TxtIDPref.Length == 0 ? "" : ".") + SrchItemInfo.TxtPropertyName);


               }
               else// Это список 
               {
                   //Рекурсия
                   GetWordField(Res, SrchItemInfo.ListType,
                               IDPref + (IDPref.Length == 0 ? "" : ".") + p.Name,
                               TagPatch + (TagPatch.Length == 0 ? "" : ".") + tagName.TagName,
                               TxtIDPref + (TxtIDPref.Length == 0 ? "" : ".") + SrchItemInfo.TxtPropertyName);
               }
           }
       }

       static void GetLinkFieldAttribute(List<LinkFieldAttribute> result, Type entityType)
       {
           if (null == entityType)
               return;

           //проходимся по свойствам - формируем для помеченных свойств SentenceWordField
           PropertyInfo[] typeProperties = entityType.GetProperties();
           foreach (PropertyInfo item in typeProperties)
           {
               LinkFieldAttribute atr = SrchInfo.GetLinkFieldAttribute(item);
               
               if (null != atr)
               {
                   result.Add(atr);
                   GetLinkFieldAttribute(result, item.PropertyType);

               }
           }
 

       }
        
    }
}
